package self_cache

import (
	"fmt"
	"sync"
	"time"
)

type Cache struct {
	*cache
}

type Item struct {
	Data       interface{}
	Expiration int64
}

type cache struct {
	lock             sync.RWMutex
	mp               map[string]Item
	clearTime        time.Duration // 定期清理过期数据的时间
	DefaultCacheTime time.Duration // 设置的默认过期时间
	NeverExpire      time.Duration
	ticker           *time.Ticker // 用于定期触发清理任务的Ticker
}

func NewCache(clearTime, defaultCacheTime time.Duration) Cache {
	c := &cache{
		mp:               make(map[string]Item),
		clearTime:        clearTime,
		DefaultCacheTime: defaultCacheTime,
		NeverExpire:      -1,
	}
	c.startClear() // 启动定期清理任务
	return Cache{
		c,
	}
}

func (c *cache) Set(key string, value interface{}, expiration time.Duration) {
	c.lock.Lock()
	defer c.lock.Unlock()
	var expirationRes int64
	if expiration > 0 {
		expirationRes = time.Now().Add(expiration).UnixNano()
	}
	c.mp[key] = Item{
		Data:       value,
		Expiration: expirationRes,
	}
}
func (c *cache) SetByExpireInt(key string, value interface{}, expiration int64) {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.mp[key] = Item{
		Data:       value,
		Expiration: expiration,
	}
}
func (c *cache) Get(key string) (interface{}, int64, bool) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	data, ok := c.mp[key]
	if !ok {
		return nil, -2, false
	}
	if data.Expiration > 0 && time.Now().UnixNano() > data.Expiration { // 过期了，删除数据
		delete(c.mp, key)
		return nil, -2, false
	}
	if data.Expiration == 0 {
		return data.Data, -1, true
	}
	return data.Data, data.Expiration, true
}
func (c *cache) TTL(key string) int64 {
	c.lock.RLock()
	defer c.lock.RUnlock()
	data, ok := c.mp[key]
	if !ok { //不存在
		return -2
	}
	if data.Expiration > 0 && time.Now().UnixNano() > data.Expiration { // 过期了，删除数据
		delete(c.mp, key)
		return -2
	}
	if data.Expiration == 0 {
		return -1 //永不过期
	}
	return data.Expiration
}
func (c *cache) startClear() {
	c.ticker = time.NewTicker(c.clearTime)
	go func() {
		for range c.ticker.C {
			c.clear()
		}
	}()
}

func (c *cache) clear() {
	fmt.Print("clear self_cache start")
	c.lock.Lock()
	defer c.lock.Unlock()
	now := time.Now().UnixNano()
	for key, item := range c.mp {
		if item.Expiration > 0 && now > item.Expiration {
			delete(c.mp, key)
		}
	}
}
func (c *cache) ScanAll() map[string]Item {
	c.lock.RLock()
	defer c.lock.RUnlock()
	res := make(map[string]Item, len(c.mp))
	now := time.Now().UnixNano()
	for k, v := range c.mp {
		if v.Expiration > 0 && now > v.Expiration {
			continue
		}
		res[k] = v
	}
	return res
}
